#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>


struct gpio_desc {
	unsigned int group; //描述gpio的组号
	unsigned int num[16];//描述gpio的组内的编号
};
static struct gpio_desc gpio_val[6];

//初始化gpio管脚编号
static void gpio_init (void)
{
	int i, j;
	for (i = 0; i < 6; i++) {
		gpio_val[i].group = i;
		for (j = 0; j < 16; j++) {
			gpio_val[i].num[j] = 496 - 16*i + j;
		}
	}
}

//根据组号和组内编号获取数字
static unsigned int gpio_get (struct gpio_desc gpios)
{
	return gpio_val[gpios.group].num[gpios.num[0]];
}

//根据用户自己的需求配置节点文件下的属性文件
static int gpio_write (char *pptr, char *attr, char *val)
{
	int fd, len;
	char file_path[256];
	//file_path: /sys/class/gpio/gpio497/attr(direction/edge...)
	sprintf (file_path, "%s/%s", pptr, attr);
	if (0 > (fd = open (file_path, O_WRONLY)))
	{
		perror ("open file_path error!");
		return -1;
	}
	len = strlen (val);
	if (len != write (fd, val, len)) {
		perror ("write val error!");
		close (fd);
		return -1;
	}
	close (fd);
	return 0;
}


int main (int argc, char **argv)
{
	struct gpio_desc gpios;
	char gpio_num[6];
	char pptr[128];
	char buff[256];
	int gpio_value_fd;
	struct pollfd fds[1];

	if ((argc != 3) || (atoi(argv[1]) != 4) || ((atoi(argv[2]) != 1) && (atoi(argv[2])) != 3))
	{
		printf ("usage: ./key <gpio group> <gpio num>\n");
		printf ("key1: ./key 4 1\n");
		printf ("key2: ./key 4 3\n");
		exit (-1);
	}

	//初始化gpio管脚编号
	gpio_init ();
	
	//根据用户输入的组号和管脚编号决定往/sys/class/gpio/export
	gpios.group = atoi(argv[1]);
	gpios.num[0] = atoi(argv[2]);
	sprintf (gpio_num, "%d", gpio_get(gpios));
	sprintf (pptr, "/sys/class/gpio/gpio%s", gpio_num);


	if (access(pptr, F_OK)) {
		int fd, len;
		if(0 > (fd = open ("/sys/class/gpio/export", O_WRONLY))) {
			perror ("open");
			exit (-1);
		}
		len = sizeof (gpio_num);
		if (len != write (fd, gpio_num, len)) {
			perror ("write error");
			close (fd);
			exit (-1);
		}
	}
	//配置节点文件下的属性文件 edge 和 direction
	if (-1 == gpio_write (pptr, "edge", "both")) {
		printf ("config gpio edge attribute error!\n");
		exit (-1);
	}
	if (-1 == gpio_write (pptr, "direction", "in")) {
		printf ("config gpio direction attribute error!\n");
		exit (-1);
	}

	memset (buff, 0, sizeof(buff)/sizeof(char));
	sprintf (buff, "%s/%s", pptr, "value");
	//当中断发生时, 唤醒poll进程读取value的值
	gpio_value_fd = open (buff, O_RDONLY);
	if (gpio_value_fd == -1) {
		perror ("open value file error");
		exit (-1);
	}

	fds[0].fd = gpio_value_fd;
	fds[0].events = POLLPRI;
	int ret = read (gpio_value_fd, buff, 10);
	if (-1 == ret) {
		perror ("read value file error!");
		exit (-1);
	}

	printf ("please press the key1 or key2...\n");
	while (1) {
		ret = poll(fds, 1, -1);
		if (-1 == ret) {
			perror ("poll error");
			exit (-1);
		}
		if (fds[0].revents & POLLPRI) {
			ret = lseek (gpio_value_fd, 0, SEEK_SET);
			if (-1 == ret) {
				perror ("lseek error");
			}

			memset (buff, 0, sizeof(buff)/sizeof(char));
			ret = read (gpio_value_fd, buff, 10);
			if (-1 == ret) {
				perror ("read error!");
			}	
			printf ("key interrupt is occured!\n");
			printf ("key value is = %s\n\n", buff);
		}
	}
	return 0;
}
